/*!
 * @file    	apm32f10x_sdio.c
 * @brief	 	This file provides all the SDIO firmware functions
 *
 * @details	
 *
 * @version		V1.0.0
 * @author  	
 * @date    	2019-8-6
 *
 */
#include "apm32f10x_sdio.h"
#include "apm32f10x_rcm.h"

/* ------------ SDIO registers bit address in the alias region ----------- */
#define SDIO_OFFSET                (SDIO_BASE - PERIPH_BASE)

/* --- CLKCR Register ---*/

/* Alias word address of CLKEN bit */
#define CLKCR_OFFSET              (SDIO_OFFSET + 0x04)
#define CLKEN_BitNumber           0x08
#define CLKCR_CLKEN_BB            (PERIPH_BB_BASE + (CLKCR_OFFSET * 32) + (CLKEN_BitNumber * 4))

/* --- CMD Register ---*/

/* Alias word address of SDIOSUSPEND bit */
#define CMD_OFFSET                (SDIO_OFFSET + 0x0C)
#define SDIOSUSPEND_BitNumber     0x0B
#define CMD_SDIOSUSPEND_BB        (PERIPH_BB_BASE + (CMD_OFFSET * 32) + (SDIOSUSPEND_BitNumber * 4))

/* Alias word address of ENCMDCOMPL bit */
#define ENCMDCOMPL_BitNumber      0x0C
#define CMD_ENCMDCOMPL_BB         (PERIPH_BB_BASE + (CMD_OFFSET * 32) + (ENCMDCOMPL_BitNumber * 4))

/* Alias word address of NIEN bit */
#define NIEN_BitNumber            0x0D
#define CMD_NIEN_BB               (PERIPH_BB_BASE + (CMD_OFFSET * 32) + (NIEN_BitNumber * 4))

/* Alias word address of ATACMD bit */
#define ATACMD_BitNumber          0x0E
#define CMD_ATACMD_BB             (PERIPH_BB_BASE + (CMD_OFFSET * 32) + (ATACMD_BitNumber * 4))

/* --- DCTRL Register ---*/

/* Alias word address of DMAEN bit */
#define DCTRL_OFFSET              (SDIO_OFFSET + 0x2C)
#define DMAEN_BitNumber           0x03
#define DCTRL_DMAEN_BB            (PERIPH_BB_BASE + (DCTRL_OFFSET * 32) + (DMAEN_BitNumber * 4))

/* Alias word address of RWSTART bit */
#define RWSTART_BitNumber         0x08
#define DCTRL_RWSTART_BB          (PERIPH_BB_BASE + (DCTRL_OFFSET * 32) + (RWSTART_BitNumber * 4))

/* Alias word address of RWSTOP bit */
#define RWSTOP_BitNumber          0x09
#define DCTRL_RWSTOP_BB           (PERIPH_BB_BASE + (DCTRL_OFFSET * 32) + (RWSTOP_BitNumber * 4))

/* Alias word address of RWMOD bit */
#define RWMOD_BitNumber           0x0A
#define DCTRL_RWMOD_BB            (PERIPH_BB_BASE + (DCTRL_OFFSET * 32) + (RWMOD_BitNumber * 4))

/* Alias word address of SDIOEN bit */
#define SDIOEN_BitNumber          0x0B
#define DCTRL_SDIOEN_BB           (PERIPH_BB_BASE + (DCTRL_OFFSET * 32) + (SDIOEN_BitNumber * 4))

/*!
 * @brief		Reset sdio peripheral registers to their default reset values
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_Reset(void)
{
	SDIO->PWR = 0x00000000;
	SDIO->CLKCTRL = 0x00000000;
	SDIO->ARG = 0x00000000;
	SDIO->CMD = 0x00000000;
	SDIO->DTMR = 0x00000000;
	SDIO->DLEN = 0x00000000;
	SDIO->DCTRL = 0x00000000;
	SDIO->INTC = 0x00C007FF;
	SDIO->MASK = 0x00000000;
}

/*!
 * @brief		Config the SDIO peripheral according to the specified parameters in the configStruct
 *
 * @param		configStruct: pointer to a SDIO_ConfigStruct_T structure
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_Config(SDIO_ConfigStruct_T* configStruct)
{
	uint32_t tmp = 0;

	/* Get the SDIO CLKCR value */
	tmp = SDIO->CLKCTRL;

	/* Clear registers bits */
	tmp &= 0xFFFF8100;

	tmp |= (configStruct->ClockDiv  | configStruct->ClockPowerSave | configStruct->ClockBypass | configStruct->BusWide | 
	configStruct->ClockEdge | configStruct->HardwareFlowControl); 

	/* Write to SDIO CLKCTRL */
	SDIO->CLKCTRL = tmp;
}

/*!
 * @brief		Fills each SDIO_ConfigStruct_T member with its default value
 *
 * @param		configStruct: pointer to a SDIO_ConfigStruct_T structure which will be initialized
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_StructInit(SDIO_ConfigStruct_T* configStruct)
{
  /* SDIO_InitStruct members default value */
  configStruct->ClockDiv = 0x00;
  configStruct->ClockEdge = SDIO_CLOCK_EDGE_RISING;
  configStruct->ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
  configStruct->ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
  configStruct->BusWide = SDIO_BUSWIDE_1B;
  configStruct->HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
}

/*!
 * @brief		Enables the SDIO clock
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_Clock_Enable(void)
{
	*(__IO uint32_t *) CLKCR_CLKEN_BB = (uint32_t)SET;
}

/*!
 * @brief		Disables the SDIO clock
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_Clock_Disable(void)
{
	*(__IO uint32_t *) CLKCR_CLKEN_BB = (uint32_t)RESET;
}

/*!
 * @brief		Sets the power status of the controller
 *
* @param		powerState: new state of the Power state
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_SetPowerState(SDIO_POWER_STATE_T powerState)
{
	SDIO->PWR &= 0xFFFFFFFC;
	SDIO->PWR |= powerState;
}

/*!
 * @brief		Reads the SDIO power state
 *
 * @param		None
 *
 * @retval		The new state SDIO power
 *
 * @note 
 */
uint32_t SDIO_ReadPowerState(void)
{
	return (SDIO->PWR & (~0xFFFFFFFC));
}

/*!
 * @brief		Enables the specified SDIO interrupt
 *
 * @param		interrupt: Select the SDIO interrupt source
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_INT_Enable(SDIO_INT_T interrupt)
{
	SDIO->MASK |= interrupt;
}

/*!
 * @brief		Disables the specified SDIO interrupt
 *
 * @param		interrupt: Select the SDIO interrupt source
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_INT_Disable(SDIO_INT_T interrupt)
{
	SDIO->MASK &= ~interrupt;
}

 
void SDIO_DMA_Enable(void)
{
	*(__IO uint32_t *) DCTRL_DMAEN_BB = (uint32_t)SET;
}

/*!
 * @brief		Disables the SDIO DMA request
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_DMA_Disable(void)
{
	*(__IO uint32_t *) DCTRL_DMAEN_BB = (uint32_t)RESET;
}

/*!
 * @brief		Configs the SDIO Command and send the command
 *
 * @param		configStruct: pointer to a SDIO_CMD_ConfigStruct_T structure
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_Send_Command(SDIO_CMD_ConfigStruct_T *configStruct)
{
	uint32_t tmpreg = 0;

	/* Set the SDIO Argument value */
	SDIO->ARG = configStruct->Argument;
	/* Get the SDIO CMD value */
	tmpreg = SDIO->CMD;
	tmpreg &= 0xFFFFF800;
	tmpreg |= (uint32_t)configStruct->CmdIndex | configStruct->Response
		   | configStruct->Wait | configStruct->CPSM;
	SDIO->CMD = tmpreg;
}

/*!
 * @brief		Fills each SDIO_CMD_ConfigStruct_T member with its default value
 *
 * @param		configStruct: pointer to a SDIO_CMD_ConfigStruct_T structure which will be initialized
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_CmdStructInit(SDIO_CMD_ConfigStruct_T* configStruct)
{
  /* SDIO_CMD_ConfigStruct_T members default value */
  configStruct->Argument = 0x00;
  configStruct->CmdIndex = 0x00;
  configStruct->Response = SDIO_RESPONSE_NO;
  configStruct->Wait = SDIO_WAIT_NO;
  configStruct->CPSM = SDIO_CPSM_DISABLE;
}

/*!
 * @brief		Reads the SDIO command response
 *
 * @param		None
 *
 * @retval		The command index of the last command response received
 *
 * @note 
 */
uint8_t SDIO_ReadCommandResponse(void)
{
	return (uint8_t)(SDIO->CMDRES);
}

/*!
 * @brief		Reads the SDIO response
 *
* @param		response: Specifies the SDIO response register
 *
 * @retval		The Corresponding response register value
 *
 * @note 
 */
uint32_t SDIO_ReadResponse(SDIO_RES_T response)
{
  __IO uint32_t tmp = 0;

  tmp = ((uint32_t)(SDIO_BASE + 0x14)) + response;
  
  return (*(__IO uint32_t *) tmp); 
}

/*!
 * @brief		Configs the SDIO Dataaccording to the specified parameters in the configStruct
 *
 * @param		configStruct: pointer to a SDIO_DataConfigStruct_T structure
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_DataConfig(SDIO_DataConfigStruct_T* configStruct)
{
	uint32_t tmpreg = 0;

	SDIO->DTMR = configStruct->DataTimeOut;

	SDIO->DLEN = configStruct->DataLength;

	tmpreg = SDIO->DCTRL;

	tmpreg &= 0xFFFFFF08;

	tmpreg |= (uint32_t)configStruct->DataBlockSize | configStruct->TransferDir
		   | configStruct->TransferMode | configStruct->DPSM;

	SDIO->DCTRL = tmpreg;
}

/*!
 * @brief		Fills each SDIO_DataConfigStruct_T member with its default value
 *
 * @param		configStruct: pointer to a SDIO_DataConfigStruct_T structure which will be initialized
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_DataStructInit(SDIO_DataConfigStruct_T* configStruct)
{
  configStruct->DataTimeOut = 0xFFFFFFFF;
  configStruct->DataLength = 0x00;
  configStruct->DataBlockSize = SDIO_DATA_BLOCKSIZE_1B;
  configStruct->TransferDir = SDIO_TRANSFER_DIR_TOCARD;
  configStruct->TransferMode = SDIO_TRANSFER_MODE_BLOCK;
  configStruct->DPSM = SDIO_DPSM_DISABLE;
}

/*!
 * @brief		Reads the SDIO Data counter
 *
* @param		None
 *
 * @retval		The SDIO Data counter register value
 *
 * @note 
 */
uint32_t SDIO_ReadDataCounter(void)
{
	return SDIO->DCNT;
}

/*!
 * @brief		Reads the SDIO Data
 *
* @param		None
 *
 * @retval		The SDIO FIFO register value
 *
 * @note 
 */
uint32_t SDIO_ReadData(void)
{
	return SDIO->FIFO;
}

/*!
 * @brief		Reads the SDIO FIFO count value
 *
* @param		None
 *
 * @retval		The SDIO FIFO count value
 *
 * @note 
 */
uint32_t SDIO_ReadFIFOCount(void)
{
	return SDIO->FIFOCNT;
}

/*!
 * @brief		Enables SDIO start read wait
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_StartReadWait_Enable(void)
{
	*(__IO uint32_t *) DCTRL_RWSTART_BB = (uint32_t) SET;
}

/*!
 * @brief		Disables SDIO start read wait
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_StartReadWait_Disable(void)
{
	*(__IO uint32_t *) DCTRL_RWSTART_BB = (uint32_t) RESET;
}

/*!
 * @brief		Sets the read wait interval
 *
* @param		readWaitMode: SDIO read Wait Mode
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_SetSDIOReadWaitMode(SDIO_READ_WAIT_MODE_T readWaitMode)
{ 
  *(__IO uint32_t *) DCTRL_RWMOD_BB = readWaitMode;
}
/*!
 * @brief		Enables SDIO SD I/O Mode Operation
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_IO_Enable(void)
{
  *(__IO uint32_t *) DCTRL_SDIOEN_BB = (uint32_t)SET;
}

/*!
 * @brief		Disables SDIO SD I/O Mode Operation
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_IO_Disable(void)
{
  *(__IO uint32_t *) DCTRL_SDIOEN_BB = (uint32_t)RESET;
}

/*!
 * @brief		Ensables SDIO SD I/O Mode suspend command sending
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_SendSDIOSuspend_Enable(void)
{
	*(__IO uint32_t *) CMD_SDIOSUSPEND_BB = (uint32_t)SET;
}

/*!
 * @brief		Disables SDIO SD I/O Mode suspend command sending
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_SendSDIOSuspend_Disable(void)
{
	*(__IO uint32_t *) CMD_SDIOSUSPEND_BB = (uint32_t)RESET;
}

/*!
 * @brief		Enables the command completion signal
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_CommandCompletion_Enable(void)
{
	*(__IO uint32_t *) CMD_ENCMDCOMPL_BB = (uint32_t)SET;
}

/*!
 * @brief		Disables the command completion signal
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_CommandCompletion_Disable(void)
{
	*(__IO uint32_t *) CMD_ENCMDCOMPL_BB = (uint32_t)RESET;
}

/*!
 * @brief		Enables the CE-ATA interrupt
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_CEATAINT_Enable(void)
{
	*(__IO uint32_t *) CMD_NIEN_BB = (uint32_t)((~((uint32_t)SET)) & ((uint32_t)0x1));
}

/*!
 * @brief		Disables the CE-ATA interrupt
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_CEATAINT_Disable(void)
{
	*(__IO uint32_t *) CMD_NIEN_BB = (uint32_t)((~((uint32_t)RESET)) & ((uint32_t)0x1));
}

/*!
 * @brief		Ensables Sends CE-ATA command
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_SendCEATA_Enable(void)
{
	*(__IO uint32_t *) CMD_ATACMD_BB = (uint32_t)SET;
}

/*!
 * @brief		Disables Sends CE-ATA command
 *
 * @param		None
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_SendCEATA_Disable(void)
{
	*(__IO uint32_t *) CMD_ATACMD_BB = (uint32_t)RESET;
}

/*!
 * @brief		Reads the specified SDIO flag
 *
 * @param		flag: Select the flag to read
 *
 * @retval		The new state of SDIO flag
 *
 * @note 
 */
uint8_t SDIO_ReadFlag(SDIO_FLAG_T flag)
{
	uint8_t bitstatus = RESET;

	if ((SDIO->STS & flag) != (uint32_t)RESET)
	{
		bitstatus = SET;
	}
	else
	{
		bitstatus = RESET;
	}
	return bitstatus;
}

/*!
 * @brief		Clears the specified SDIO flag
 *
 * @param		flag: Select the flag to clear
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_ClearFlag(SDIO_FLAG_T flag)
{ 
  SDIO->INTC = flag;
}

/*!
 * @brief		Reads the specified SDIO Interrupt flag
 *
 * @param		interrupt: Select the SDIO interrupt source
 *
 * @retval		The new state of SDIO interrupt
 *
 * @note 
 */
uint8_t SDIO_ReadINTFlag(SDIO_INT_T interrupt)
{
	uint8_t bitstatus = RESET;

	if ((SDIO->STS & interrupt) != (uint32_t)RESET)  
	{
		bitstatus = SET;
	}
	else
	{
		bitstatus = RESET;
	}
	return bitstatus;
}

/*!
 * @brief		Clears the specified SDIO Interrupt pending bits
 *
 * @param		interrupt: Select the SDIO interrupt source
 *
 * @retval		None
 *
 * @note 
 */
void SDIO_ClearITPendingBit(SDIO_INT_T interrupt)
{    
  SDIO->INTC = interrupt;
}
